#ifndef ODBC_TYPES_HPP
#define ODBC_TYPES_HPP

#include "odbc/details/os_sql_wrapper.hpp"
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <string>
#include <vector>
#include <cassert>

namespace odbc
{

namespace details
{

	// TODO: IMplement support for boolean
	// TODO: Implement support for blobs

/// \ingroup OdbcLibrary
/// \brief It is the base class for built-in C++ types, providing the common operations. 
/// \details The type traits for built-in types will use this class as base class, providing for it the type id (see the 
///		ODBC API documentation regarding the type id values and their correspondence to C++ types). 
///	\n
/// All the operations here are similar for the built in types. As it can be seen, the string type traits class is not 
///	derived from here, because the provided operations are implemented in a (very) different way. 
///
/// \param T the C++ type of the value supported
/// \param TID the ODBC code of the ODBC type associated with the C++ type.
/// \param SQLTID the corresponding SQL type id.
template< typename T, int TID, int SQLTID>
struct built_in_type_traits
{
	/// \brief It provides the ODBC type id. Some ODBC API operations (like column or parameter binding) need this type 
	///		id to be provided as parameter.
	enum 
	{ 
		odbc_type_id = TID,
		sql_type_id = SQLTID
	};

	/// \brief The C++ type of the value, corresponding to the ODBC type.
	typedef T value_type;

	/// \brief The type of the buffer, needed when binding a column or a parameter to an SQL statement. 
	/// \details For the built-in types, the buffer is the value itself. It means that when providing the void* buffer 
	///		address, it is simply needed to provide the address of a value where the ODBC driver will put the value (see 
	///		buffer_to_void static member).
	typedef value_type buffer_type;

	/// \brief The type of the size.
	typedef std::size_t size_type;

	/// \brief It converts the given buffer to the value. In case of built-in type, it is trivial, since the buffer is 
	///		the value itself.
	/// \param buffer the buffer to be converted.
	/// \return the value resulted from the conversion. In the case of built-in types, it is the buffer itself.
	static value_type convert( const buffer_type& buffer) 
	{ 
		return buffer; 
	}

	/// \brief It sets the specified value to the specified buffer. In this case, of built-in types, it means copying 
	///		the value to the buffer through a simple assignment.
	/// \param value the value to be converted
	/// \param buffer the target buffer, that contains the result of the
	/// conversion.
	static void convert( const value_type& value, buffer_type& buffer) 
	{ 
		buffer = value; 
	}

	/// \brief It converts the specified buffer to a void* pointer, required by the ODBC API binding functions.
	/// \param buffer the buffer to be converted
	/// \return the buffer address
	static void* buffer_to_void( buffer_type& buffer) 
	{ 
		return static_cast< void*>(&buffer); 
	}

	/// \brief It converts the specified buffer to a void* pointer, required by the ODBC API binding functions.
	/// \param buffer the buffer to be converted
	/// \return the buffer address
	static const void* buffer_to_void( const buffer_type& buffer) 
	{ 
		return static_cast< const void*>(&buffer); 
	}

	/// \brief It calculates the size of the given buffer. 
	/// \details In case of the built-in type, the size is the same for all the buffers, so the parameter is ignored. It 
	///		is still specified in function interface, to provide common interface of the type traits for all the types.
	static size_type size_of_buffer( const buffer_type& ) 
	{ 
		return sizeof( buffer_type); 
	}

	/// \brief It gets the size of the value type.
	/// \param value The value to be measured.
	/// \details This information is used for binding output and input/output parameters to a statement, so the length 
	///		indicator is properly set.
	/// \note Because built-in types have the corresponding buffer the value itself (also, a fixed size value), the 
	///		method just returns the memory size of the value.
	static size_type size_of_value( const value_type& value)
	{
		return sizeof( value);
	}

	/// \brief It creates and initializes a buffer.
	/// \param size not used
	/// \return the created buffer
	/// \note Because the buffer is a fixed size type, the size parameter is ignored completely.
	static buffer_type create_buffer( size_type /*size*/ = 0) 
	{ 
		return buffer_type( 0); 
	}
	
	/// \brief It destroys the specified buffer. In the case of built-in types, since the buffer is not dynamically 
	///		allocated, this function does nothing.
	/// \param buffer the buffer to be destroyed
	static void destroy_buffer( const buffer_type& buffer) { }
};

} // namespace details


/// \ingroup OdbcLibrary
/// \brief It is the class used for basic, low level operations, linking the ODBC API types to C++ types.
///
/// Because ODBC library supports a limited set of value types, this template class is left undefined. Only its 
///	specializations for the supported types are defined, so using ODBC functionality for C++ types other than supported 
///	ones will surely give compiler errors.
///
/// \param T the type of the C++ value to be mapped to a corresponding ODBC type.
template <typename T> struct type_traits { };

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ char type.
template<> struct type_traits< char>
	: public details::built_in_type_traits< char, SQL_C_STINYINT, SQL_TINYINT>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ unsigned char type.
template<> struct type_traits< unsigned char>
	: public details::built_in_type_traits< unsigned char, SQL_C_UTINYINT, SQL_TINYINT>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ short int type.
template<> struct type_traits< short>
	: public details::built_in_type_traits< short, SQL_C_SHORT, SQL_SMALLINT> 
{ 
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ wchar_t type.
template<> struct type_traits< wchar_t>
: public details::built_in_type_traits< wchar_t, SQL_C_ULONG, SQL_INTEGER> 
{ 
};


/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ unsigned short int type.
template<> struct type_traits< unsigned short>
	: public details::built_in_type_traits< unsigned short, SQL_C_USHORT, SQL_SMALLINT>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ int type.
template<> struct type_traits< int>
	: public details::built_in_type_traits< int, SQL_C_SLONG, SQL_INTEGER>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ unsigned int type.
template<> struct type_traits< unsigned int>
	: public details::built_in_type_traits< unsigned int, SQL_C_ULONG, SQL_INTEGER>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ long type.
template<> struct type_traits< long>
	: public details::built_in_type_traits< long, SQL_C_SLONG, SQL_INTEGER>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ unsigned long type.
template<> struct type_traits< unsigned long>
	: public details::built_in_type_traits< unsigned long, SQL_C_ULONG, SQL_INTEGER>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ float type.\n
template<> struct type_traits< float>
	: public details::built_in_type_traits< float, SQL_C_FLOAT, SQL_FLOAT>
{
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ double type.\n
template<> struct type_traits< double>
	: public details::built_in_type_traits< double, SQL_C_DOUBLE, SQL_REAL>
{
};


/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ Boost Library date class.
template<> struct type_traits< boost::gregorian::date>
{
	/// \brief It provides the ODBC type id. Some ODBC API operations (like column or parameter binding) need this type 
	///		id to be provided as parameter.
	enum 
	{
		odbc_type_id = SQL_C_TYPE_DATE,
		sql_type_id = SQL_TYPE_DATE
	};

	/// \brief The C++ type of the value, corresponding to the ODBC type.
	typedef boost::gregorian::date value_type;

	/// \brief The type of the buffer, needed when binding a column or a parameter to an SQL statement. For the date 
	///		type, the buffer is directly the memory holding the SQL_DATE_STRUCT structure, which has fixed size and is a 
	///		POD.
	typedef SQL_DATE_STRUCT buffer_type;

	/// \brief The type of the size.
	typedef std::size_t size_type;

	/// \brief Converts the given buffer to the value. In case of built-in type, it is trivial, since the buffer is the 
	///		value itself.
	/// \param buffer the buffer to be converted.
	/// \return the value resulted from the conversion. 
	static value_type convert( const buffer_type& buffer)
	{
		value_type date( buffer.year, buffer.month, buffer.day);
		return date;
	}

	/// \brief It sets the specified value to the specified buffer. 
	/// \param value the value to be converted
	/// \param buffer the target buffer, that contains the result of the conversion.
	static void convert( const value_type& value, buffer_type& buffer)
	{
		buffer.year = value.year();
		buffer.month = value.month();
		buffer.day = value.day();
	}

	/// \brief It converts the specified buffer to a void* pointer, required by the ODBC API binding functions.
	/// \param buffer the buffer to be converted
	/// \return the buffer address
	static void* buffer_to_void( buffer_type& buffer)
	{
		return static_cast< void*>( &buffer);
	}

	/// \brief It converts the specified buffer to a void* pointer, required by the ODBC API binding functions.
	/// \param buffer the buffer to be converted
	/// \return the buffer address
	static const void* buffer_to_void( const buffer_type& buffer)
	{
		return static_cast< const void*>( &buffer);
	}

	/// \brief It calculates the size of the given buffer. In case of the built-in type, the size is tha same for all 
	///		the buffers, so the parameter is ignored. It is still specified in function interface, to provide common 
	///		interface of the type traits for all the types.
	static size_type size_of_buffer( const buffer_type& buffer)
	{
		return sizeof( buffer);
	}

	/// \brief It gets the size of the value type.
	/// \param value The value to be measured.
	/// \details This information is used for binding output and input/output parameters to a statement, so the length 
	///		indicator is properly set.
	/// \note Considering the usage of the method and that the buffer is fixed size (but differs in size from the value, 
	///		the size of the buffer is returned).
	static size_type size_of_value( const value_type& /*value*/)
	{
		return sizeof( buffer_type);
	}


	/// \brief It creates a buffer using the specified size. If no size is specified, a default one is used. The size 
	///		should be greater than 0. The created buffer will be filled with zeros.
	/// \param size the size of the buffer to be created
	/// \return the created buffer
	/// \note Because the buffer is a fixed size structure, the size parameter is ignored completely.
	static buffer_type create_buffer( size_type = 0)
	{
		buffer_type buffer = { 0, 0, 0 };
		return buffer;
	}

	/// \brief It destroys the specified buffer. In the case of date structure, since the buffer is not dynamically 
	///		allocated, this function does nothing.
	/// \param buffer the buffer to be destroyed
	static void destroy_buffer( const buffer_type& )
	{
	}
};

/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ Boost Library date/time class.
template<> struct type_traits< boost::posix_time::ptime>
{
	/// \brief It provides the ODBC type id. Some ODBC API operations (like column or parameter binding) need this type 
	///		id to be provided as parameter.
	enum 
	{ 
		odbc_type_id = SQL_C_TYPE_TIMESTAMP,
		sql_type_id = SQL_TYPE_TIMESTAMP
	};

	/// \brief The C++ type of the value, corresponding to the ODBC type.
	typedef boost::posix_time::ptime value_type;

	/// \brief The type of the buffer, needed when binding a column or a parameter to an SQL statement. For the date 
	///		type, the buffer is directly the memory holding the SQL_DATE_STRUCT structure, which has fixed size and is a 
	///		POD.
	typedef SQL_TIMESTAMP_STRUCT buffer_type;

	/// \brief The type of the size.
	typedef std::size_t size_type;

	/// \brief Converts the given buffer to the value. In case of built-in type, it is trivial, since the buffer is the 
	///		value itself.
	/// \param buffer the buffer to be converted.
	/// \return the value resulted from the conversion. 
	static value_type convert( const buffer_type& buffer)
	{
		using namespace boost::gregorian;
		using namespace boost::posix_time;

		value_type result( 
			date( buffer.year, buffer.month, buffer.day),
			hours( buffer.hour) 
				+ minutes( buffer.minute)
				+ seconds( buffer.second));
		return result;
	}

	/// \brief It sets the specified value to the specified buffer. 
	/// \param value the value to be converted
	/// \param buffer the target buffer, that contains the result of the
	///		conversion.
	static void convert( const value_type& value, buffer_type& buffer)
	{
		buffer.year = value.date().year();
		buffer.month = value.date().month();
		buffer.day = value.date().day();
		buffer.hour = (SQLUSMALLINT)value.time_of_day().hours();
		buffer.minute = (SQLUSMALLINT)value.time_of_day().minutes();
		buffer.second = (SQLUSMALLINT)value.time_of_day().seconds();
	}


	/// \brief It converts the specified buffer to a void* pointer, required by the ODBC API binding functions.
	/// \param buffer the buffer to be converted
	/// \return the buffer address
	static void* buffer_to_void( buffer_type& buffer)
	{
		return static_cast< void*>( &buffer);
	}

	/// \brief It converts the specified buffer to a void* pointer, required by the ODBC API binding functions.
	/// \param buffer the buffer to be converted
	/// \return the buffer address
	static const void* buffer_to_void( const buffer_type& buffer)
	{
		return static_cast< const void*>( &buffer);
	}

	/// \brief It calculates the size of the given buffer. In case of the built-in type, the size is tha same for all 
	///		the buffers, so the parameter is ignored. It is still specified in function interface, to provide common 
	///		interface of the type traits for all the types.
	static size_type size_of_buffer( const buffer_type& buffer)
	{
		return sizeof( buffer);
	}

	/// \brief It gets the size of the value type.
	/// \param value The value to be measured.
	/// \details This information is used for binding output and input/output parameters to a statement, so the length 
	///		indicator is properly set.
	/// \note Considering the usage of the method and that the buffer is fixed size (but differs in size from the value, 
	///		the size of the buffer is returned).
	static size_type size_of_value( const value_type& /*value*/)
	{
		return sizeof( buffer_type);
	}


	/// \brief It creates a buffer using the specified size. If no size is specified, a default one is used. The size 
	///		should be greater than 0.
	/// \details The created buffer will be filled with zeros.
	/// \param size the size of the buffer to be created
	/// \return the created buffer
	/// \note Because the buffer is a fixed size structure, the size parameter is ignored completely.
	static buffer_type create_buffer( size_type = 0)
	{
		buffer_type buffer = { 0, 0, 0, 0, 0, 0, 0 };
		return buffer;
	}

	/// \brief It destroys the specified buffer. 
	/// \details In the case of date structure, since the buffer is not dynamically allocated, this function does 
	///		nothing.
	/// \param buffer the buffer to be destroyed
	static void destroy_buffer( const buffer_type& )
	{
	}
};


/// \ingroup OdbcLibrary
/// \brief It is the type traits specialization for C++ standard string type.
/// \details ODBC API doesn't support directly the \code std::basic_string \endcode type, but supports instead the C 
///	style strings (char* pointers). Since this is a C++ library, it doesn't support plain C style strings, but C++ 
///	strings. For this C++ type, however, the type traits are a little bit different from the built-in type traits.
///	\n
/// The string values don't have a fixed size, so this is one more reason for which the type traits operations are so 
///	different from the other built-in types.\n
template< typename ST> struct type_traits< std::basic_string< char, ST> >
{
	/// \brief The ODBC API type id that is associated with the C++ string, needed for some ODBC API calls.
	enum 
	{ 
		odbc_type_id = SQL_C_CHAR,
		sql_type_id = SQL_CHAR
	};
	
	/// \brief The C++ string value.
	typedef std::string value_type;
	
	/// \brief The buffer that will be used for binding string value columns or parameters. The buffer is a standard 
	///		vector of bytes, which is safer and it automatically deallocates the memory at destruction time.
	typedef std::vector< char> buffer_type;

	/// \brief The type used to specify buffer sizes or value sizes.
	typedef value_type::size_type size_type;

	/// \brief Converts the specified buffer to a string value.
	/// \param buffer the buffer containing the raw data
	/// \return the resulted string value
	static value_type convert( const buffer_type& buffer)
	{
		return std::string( &buffer.front());
	}

	/// \brief Sets the specified value to the specified buffer.
	/// \param value the value to be set in the buffer
	/// \param buffer the target buffer
	static void convert( const value_type& value, buffer_type& buffer)
	{
		buffer.assign( value.begin(), value.end());
	}

	/// \brief Returns the address of the given buffer.
	/// \param buffer the buffer of which address should be obtained.
	/// \return the address of the buffer.
	static void* buffer_to_void( buffer_type& buffer)
	{
		return static_cast< void*>( &buffer.front());
	}

	/// \brief Returns the address of the given buffer.
	/// \param buffer the buffer of which address should be obtained.
	/// \return the address of the buffer.
	static const void* buffer_to_void( const buffer_type& buffer) 
	{
		return static_cast< const void*>( &buffer.front());
	}


	/// \brief It gets the size of the specified buffer.
	/// \details Since two strings can have two different size, the same issue is valid for the associated buffers, too, 
	///	so the returned size depends on the specified buffer.
	///
	/// \param buffer the buffer for which the size is requested
	/// \return the size of the specified buffer
	static size_type size_of_buffer( const buffer_type& buffer) 
	{ 
		return buffer.size(); 
	}

	/// \brief
	///		It gets the size of the value type.
	/// \param value
	///		The value to be measured.
	/// \details
	///		This information is used for binding output and input/output parameters to a statement, so the length 
	///		indicator is properly set.
	/// \note
	///		Considering the usage of the method and that the buffer is not fixed size, the size of the string is 
	///		calculated. However, the string size might be bigger than the buffer, so an additional check is made 
	///		when binding the parameter to the statement (taking the minimum size between the value and the buffer).
	static size_type size_of_value( const value_type& value)
	{
		return value.size() * sizeof( typename value_type::value_type);
	}


	/// \brief 
	///		It creates a buffer using the specified size. If no size is specified, a default one is used. The size 
	///		should be greater than 0.
	/// \details
	///		The created buffer will be filled with zeros.
	/// \param size 
	///		the size of the buffer to be created
	/// \return 
	///		the created buffer
	static buffer_type create_buffer( int size = 256) 
	{ 
		assert( size > 0); 
		return buffer_type( size, 0); 
	}

	/// \brief 
	///		It destroys the specified buffer.
	/// \details
	///		Because the memory of the buffer is managed by the standard vector class, the deallocation is made 
	///		automatically in vector's destructor and this method should do nothing more.
	/// \param the buffer to be destroyed
	static void destroy_buffer( const buffer_type& ) { }
};


/// \ingroup OdbcLibrary
/// \brief 
///		It is the type traits specialization for C++ standard wide string type.
/// \details
///		ODBC API doesn't support directly the \code std::basic_string \endcode type, but supports instead the C style 
///		strings (char* pointers). Since this is a C++ library, it doesn't support plain C style strings, but C++ 
///		strings. For this C++ type, however, the type traits are a little bit different from the built-in type traits.
///		\n
///		The string values don't have a fixed size, so this is one more reason for which the type traits operations are 
///		so different from the other built-in types.
template < typename ST> struct type_traits< std::basic_string< wchar_t, ST> >
{
	/// \brief 
	///		The ODBC API type id that is associated with the C++ wide string, needed for some ODBC API calls.
	enum 
	{ 
		odbc_type_id = SQL_C_WCHAR,
		sql_type_id = SQL_WCHAR
	};

	/// \brief The C++ wide string value.
	typedef std::wstring value_type;

	/// \brief 
	///		The buffer that will be used for binding string value columns or parameters. The buffer is a standard vector 
	///		of bytes, which is safer and it automatically deallocates the memory at destruction time.
	typedef std::vector< wchar_t> buffer_type;

	/// \brief The type used to specify buffer sizes or value sizes.
	typedef value_type::size_type size_type;

	/// \brief Converts the specified buffer to a string value.
	/// \param buffer the buffer containing the raw data
	/// \return the resulted string value
	static value_type convert( const buffer_type& buffer)
	{
		return std::wstring( &buffer.front());
	}

	/// \brief Sets the specified value to the specified buffer.
	/// \param value the value to be set in the buffer
	/// \param buffer the target buffer
	static void convert( const value_type& value, buffer_type& buffer)
	{
		buffer.assign( value.begin(), value.end());
	}

	/// \brief Returns the address of the given buffer.
	/// \param buffer the buffer of which address should be obtained.
	/// \return the address of the buffer.
	static void* buffer_to_void( buffer_type& buffer)
	{
		return static_cast< void*>( &buffer.front());
	}

	/// \brief Returns the address of the given buffer.
	/// \param buffer the buffer of which address should be obtained.
	/// \return the address of the buffer.
	static const void* buffer_to_void( const buffer_type& buffer) 
	{
		return static_cast< const void*>( &buffer.front());
	}


	/// \brief 
	///		It creates a buffer using the specified size. If no size is specified, a default one is used. The size 
	///		should be greater than 0.
	/// \details
	///		The created buffer will be filled with zeros.
	/// \param size the size of the buffer to be created
	/// \return the created buffer
	/// \note
	///		Because the buffer is a vector of \c wchar_t elements, the size of the vector should be set to half of the 
	///		specified size.
	static buffer_type create_buffer( int size = 256) 
	{ 
		assert( size > 0); 
		return buffer_type( (size >> 2), 0); 
	}

	/// \brief It destroys the specified buffer.
	/// \details 
	///		Because the memory of the buffer is managed by the standard vector class, the deallocation is made 
	///		automatically in vector's destructor and this method should do nothing more.
	/// \param the buffer to be destroyed
	static void destroy_buffer( const buffer_type& ) { }

	/// \brief It gets the size of the specified buffer.
	/// \details
	///		Since two strings can have two different sizes, the same issue is valid for the associated buffers, too, so 
	///		the returned size depends on the specified buffer.
	/// \param buffer 
	///		the buffer for which the size is requested
	/// \return 
	///		the size of the specified buffer
	static size_type size_of_buffer( const buffer_type& buffer) 
	{ 
		return buffer.size() * sizeof( buffer_type::value_type); 
	}

	/// \brief
	///		It gets the size of the value type.
	/// \param value
	///		The value to be measured.
	/// \details
	///		This information is used for binding output and input/output parameters to a statement, so the length 
	///		indicator is properly set.
	/// \note
	///		Considering the usage of the method and that the buffer is not fixed size, the size of the string is 
	///		calculated. However, the string size might be bigger than the buffer, so an additional check is made 
	///		when binding the parameter to the statement (taking the minimum size between the value and the buffer).
	static size_type size_of_value( const value_type& value)
	{
		return value.size() * sizeof( typename value_type::value_type);
	}
};

} // namespace odbc

#endif // ODBC_TYPES_HPP
